移动端的定位

定位的偏移

通常来说,移动端定位依赖于该设备系统对应的SDK所以提供的接口,这里面最容易遇到的就是坐标系问题。
常见坐标系有三种,地球坐标(WGS84,国际标准)、中国国测局地理坐标(GCJ-02,火星坐标)、百度坐标(BD09)。坐标系的选择与使用的地图类型有着很大的相关性,如果两者之间存在不对应的情况,就容易引发定位的偏移问题。
对于系统原生的SDK而言,默认返回的是WGS84坐标系下的定位。对于国内的一些定位SDK而言,高德SDK没有坐标系参数设定,在大陆和港澳地区获取的坐标系即为GCJ02坐标系,在台湾和海外地区都是WGS84坐标系;百度SDK可以自行设定坐标系参数,即返回WGS84坐标系,还是GCJ02坐标系或者BD09坐标系(如果设定的是GCJ02坐标系,它在大陆、港澳台地区获取的坐标系都是GCJ02坐标系)。
基于这种情况,假设我们采用了系统定位,辅以百度地图来呈现,就会出现偏差在1km左右物理距离的偏移(百度地图提供了Convertor来完成WGS84到BD09的转换)。

定位偏移算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
; (function () {
const pi = Math.PI;
const a = 6378245.0;
const ee = 0.00669342162296594323;

function transformLat(x, y) {
let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
+ 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
return ret;
}

function transformLon(x, y) {
let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
* Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0
* pi)) * 2.0 / 3.0;
return ret;
}


function WGS84_to_GCJ02(lat, lon) {
let dLat = transformLat(lon - 105.0, lat - 35.0),
dLon = transformLon(lon - 105.0, lat - 35.0);
const radLat = lat / 180.0 * pi,
magic = Math.sin(radLat),
sqrtMagic = Math.sqrt(1 - ee * magic * magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
return { latitude: lat + dLat, longtitude: lon + dLon };
}

function GCJ02_to_WGS84(lat, lon) {
const gps = WGS84_to_GCJ02(lat, lon);
return { latitude: lat * 2 - gps.latitude, lontitude: lon * 2 - gps.longtitude };
}
})();

精度问题

手机有三种定位方式:

  • 卫星定位:采用GPS或北斗定位系统,精度在10-100米左右,在室内无法使用,受环境限制。
  • 基站定位:根据运营商基站进行定位,具体为测算附近三个基站与用户之间的距离(信号延时,信号强度),同时将其两两相连,构成三角形,计算用户在这个三角形中的位置,然后计算用户相对于三个基站的位置偏移,取误差范围内可接受的结果。其精度受基站密度影响较大,城市50-150米,城郊100-300米,乡村200-2000米;同时信号也容易受到环境的影响。
  • WIFI:根据周围WIFI路由器的位置计算定位,精度在100-200米左右。

手写EventEmitter


手写EventEmitter

准备工作

1. 事件的创建

JS中,最简单的创建事件方法,是使用Event构造器:

1
var myEvent = new Event('event_name');

但是为了能够传递数据,就需要使用 CustomEvent 构造器:

1
2
3
4
5
6
var myEvent = new CustomEvent('event_name', {
detail:{
// 将需要传递的数据写在detail中,以便在EventListener中获取
// 数据将会在event.detail中得到
},
});

2. 事件的监听

JS的EventListener是根据事件的名称来进行监听的,比如我们在上文中已经创建了一个名称为‘event_name’ 的事件,那么当某个元素需要监听它的时候,就需要创建相应的监听器:

1
2
3
4
5
6
7
//假设listener注册在window对象上
window.addEventListener('event_name', function(event){
// 如果是CustomEvent,传入的数据在event.detail中
console.log('得到数据为:', event.detail);

// ...后续相关操作
});

至此,window对象上就有了对‘event_name’ 这个事件的监听器,当window上触发这个事件的时候,相关的callback就会执行。

3. 事件的触发

对于一些内置(built-in)的事件,通常都是有一些操作去做触发,比如鼠标单击对应MouseEvent的click事件,利用鼠标(ctrl+滚轮上下)去放大缩小页面对应WheelEvent的resize事件。
然而,自定义的事件由于不是JS内置的事件,所以我们需要在JS代码中去显式地触发它。方法是使用 dispatchEvent 去触发(IE8低版本兼容,使用fireEvent):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 首先需要提前定义好事件,并且注册相关的EventListener
var myEvent = new CustomEvent('event_name', {
detail: { title: 'This is title!'},
});
window.addEventListener('event_name', function(event){
console.log('得到标题为:', event.detail.title);
});
// 随后在对应的元素上触发该事件
if(window.dispatchEvent) {
window.dispatchEvent(myEvent);
} else {
window.fireEvent(myEvent);
}
// 根据listener中的callback函数定义,应当会在console中输出 "得到标题为: This is title!"

需要特别注意的是,当一个事件触发的时候,如果相应的element及其上级元素没有对应的EventListener,就不会有任何回调操作。
对于子元素的监听,可以对父元素添加事件托管,让事件在事件冒泡阶段被监听器捕获并执行。这时候,使用event.target就可以获取到具体触发事件的元素。


事件触发器

1.基于浏览器的CustomEvent

上述过程是在JS中触发一个事件的原理,但是相对于实际的运用不算方便,所以需要对这个事件发生器进行一下封装。
这里可以选择将事件注册在document元素上,这样在页面销毁之前,这个事件会一直保存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 类似于Vue、Angular等MVVM的事件发生器,使用CustomEvent
*/
// ES6
class _Event {
static on(type, handler) {
return document.addEventListener(type, handler)
}
static emit(type, data) {
return document.dispatchEvent(new CustomEvent(type, {
detail: data
}))
}
}

// ES5
function myEvent() {}
myEvent.on = function (type, handler) {
return document.addEventListener(type, handler);
};
myEvent.emit = function (type, data) {
return document.dispatchEvent(new CustomEvent(type, {
detail: data,
}));
};

_Event.on('search', e => {
console.log(e.detail);
});
_Event.emit('search', 'dosth ');

2.实现一个可以手动触发的Event管理器

对于一些MVVM项目来说,状态的管理不能局限于单一一个页面,页面之间的共享需要使用Shared Service Worker来实现,这时候依赖于document上的CustomEvent就比较乏力了。
于是,我们需要去设计一个Event的管理器,用它来记录各个Event的hanlders,然后用emit方法触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
var Event = (function () {
// private的_events记录每种event的handlers
var _events = {};

/**
* 注册一个Event的handler
* @param {String} evt
* @param {*} handler
*/
function on(evt, handler) {
_events[evt] = _events[evt] || [];
// 如果是没有名称的话就无法删除
_events[evt].push({
name: handler.name || 'anonymous',
handler: handler,
});
}

/**
* 触发一种Event,并传入数据
* @param {String} evt
* @param {*} args
*/
function emit(evt, args) {
if (!_events[evt]) {
return;
}
for (var i = 0, ii = _events[evt].length; i < ii; i++) {
_events[evt][i].handler(args);
}
}

/**
* 用来移除非匿名的handler
* @param {String} evt
* @param {String} name
*/
function remove(evt, name) {
if (!_events[evt]) {
return;
}
_events[evt] = _events[evt].filter(value => {
return value.name !== name;
});
}

return {
on: on,
emit: emit,
remove: remove,
};
})();

Event.on('search', function dosth(data) {
console.log(data);
});

Event.emit('search', '123');

Event.remove('search', 'dosth');

Event.emit('search', '123');

Hexo配置过程

Hexo配置过程

最早的博客是放在CSDN上的。后来发现了Hexo这个框架,将其与Git Page结合可以直接部署在Github上,确实是非常方便。于是花了几小时的时间过了一下官网的文档,并尝试了将其部署到这个Repo的上来。

配置环境

首先,使用HEXO需要具备一系列的开发环境:

安装完成后可以在termial检查node和npm是否存在。

1
2
3
$ node -v
$ npm -v
$ git --version

最后记得一定要有自己的Github帐号,要不然部署到哪里去呢?

使用Hexo

Hexo安装

在npm配置完成后,就可以进行Hexo-cli的安装

1
$ npm install -g hexo-cli

建立项目

安装完成后,就可以使用Hexo-cli来生成Hexo的项目

1
2
3
4
5
6
7
8
# 创建项目,<folder>替换为具体的文件夹名称
$ hexo init <folder>

# 进入该文件夹
$ cd <folder>

# 安装node_modules依赖
$ npm install

安装完成后,就可以使用Hexo来启动这个项目,进行测试

1
2
3
4
$ hexo server

# 或者使用简写形式
$ hexo s

如果运行成功,Hexo默认使用的是localhost:4000端口,直接进入该地址即可查看。
如果该端口已被占用,则需要更换一个端口来运行:

1
2
# 使用4001或者其他的未被占用的端口
$ hexo s -p 4001

GitHub的准备工作

这里需要分为两种情况来讨论,具体关乎branch的差别。

1.branch: master

如果要在master分支上部署网站的话,就必须建立一个名称为<yourUserName>.github.io(将你的Github账户名替换这个<yourUserName>)的Repo,然后在进行部署推送的时候,就可以使用这个Repo的master branch。

2.branch: gh-pages

如果不想要将网站部署在master分支,在某一个Repo下建立gh-pages分支,部署的时候再部署到这个分支上(部署的过程将会在后面描述)。

branch准备完成以后,需要在Github的Repo中,选择Settings -> Github Pages选项,将Source选择到你使用的branch上面,就可以打开Github Pages功能了。

Hexo在Git上部署

1.Git的配置

设置好Git的用户名和email(如果以前有设置过,可跳过)

1
2
$ git config --global user.name "yourname"
$ git config --global user.email "youremail"

2.安装部署工具

在项目路径下,安装Hexo for Git的部署工具

1
$ npm install hexo-deployer-git --save

3.修改配置文件_config.yml

配置_config.yml文件中的deploy项。(注意属性名称的冒号:后面要有一个空格)
type选择git
repo输入你github项目(即将被部署页面等内容的项目)的url
branch选择你需要的branch,比如master或者gh-pages

1
2
3
4
deploy:
type: git
repo: <url address of your github repository>
branch: master

当然也可以根据官方文档,去配置其他source的部署。

4.Hexo部署

以上配置完成后,就可以着手正式的部署了。
在项目路径下,打开terminal输入以下命令

1
2
3
4
5
6
7
8
# 生成静态文件
$ hexo generate
# 部署
$ hexo deploy

# 简写形式,g表示generate,d表示deploy
$ hexo g -d
$ hexo d -g

这样就能将Hexo生成的静态页面,推送到远端的Github上。
如果部署在master分支上,访问<yourUserName>.github.io
如果部署在gh-pages分支上,访问<yourUserName>.github.io/<repository>/